#pragma once
#include<memory>
#include<iostream>
#include<string>
#include<vector>
#include <unistd.h>
#include<cstdio>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h>
using namespace std;


//创建key
const string SEM_PATH = "./tmp/";
const int SEM_PROJ_ID  = 123;
//信号量集中信号量默认个数
const int defaultnum = 1;
//flag
#define GET_SEM (IPC_CREAT)
#define BUILD_SEM (IPC_CREAT | IPC_EXCL)

string intToHex(int num)
{
     char hex[64];
     snprintf(hex,sizeof(hex),"0x%x",num);
     return string(hex);
}


//产品类
class Semaphore
{
public: 
    Semaphore(int semid)
    :_semid(semid)
    {}
    
    ~Semaphore()
    {
        //安全性检查
        if(_semid >= 0)
        {
           int n = ::semctl(_semid,0,IPC_RMID);
           if(n < 0)
           {
              cerr << "semctl IPC_RMID error" << endl;
           }
           cout << "Semphore " << _semid << " removed " << endl;
        }
    }

    void P(int who)
    {
        PV(-1,who);
    }  

    void V(int who)
    {
        PV(1,who);
    }

    int Id() const
    {
       return _semid;
    } 
private:
     void PV(int data,int who)
     {
        //1.定义结构体
        struct sembuf sem_buf;
        sem_buf.sem_num = who;//默认信号量只有一个信号量，所以从0开始
        sem_buf.sem_op = data;//P V
        sem_buf.sem_flg = SEM_UNDO;//不关心
        //2.调用
        int n = ::semop(_semid,&sem_buf,1);//对一个信号量操作
        if(n < 0)
        {
            cerr << "semop error" << endl;
        }
     }  


private:
    int _semid;    
};

//抽象建造者
class SemaphoreBuilder
{
public:
   virtual ~SemaphoreBuilder() = default;
   //1.创建信号量集
   virtual void BuildKey() = 0;  //获得key
   virtual void SetPermission(int perm)  = 0; //获得权限
   virtual void SetSemNum(int num) = 0; //信号量集中信号量个数
   virtual void Build(int flag) = 0;  //创建信号量集
   //2.初始化信号量集
   virtual void SetInitVal(vector<int> initVal) = 0; //设置每个信号量的初始值数组下标代表信号量编号
   virtual void InitSem() = 0; 
   virtual shared_ptr<Semaphore> GetSem() = 0; //获取产品接口
};


//具体建造者
class ConcreteSemaphoreBuilder : public SemaphoreBuilder
{
public:
    ConcreteSemaphoreBuilder(){}
    virtual void BuildKey() override
    {
      //1.构建键值
      _key = ftok(SEM_PATH.c_str(),SEM_PROJ_ID);
      if(_key < 0)
      {
        cerr << "ftok key error " << endl;
        exit(1);
      }
      cout << "ftok key success: " << intToHex(_key) << endl;
    }

    virtual void SetPermission(int perm)  override
    {
        _perms = perm;
    }

    virtual void SetSemNum(int num) override 
    {
        if(num > 0)
        _num = num;
        else
          exit(2);
    }

    virtual void Build(int flag) override 
    {
        int semid = ::semget(_key,_num,flag | _perms);
        if(semid < 0)
        {
          cerr << "build sem error" << endl;
          exit(3);
        }
        cout << "build sem success : " << semid << endl;
        _sem = make_shared<Semaphore>(semid);
    }

    //2.初始化信号量集
    virtual void SetInitVal(vector<int> initVal) override 
    {
           _initVal = initVal;
    }

    virtual void InitSem() override 
    {
        //安全性检查
        if(_num > 0 && _initVal.size() == _num)
        {
           for(int i = 0 ; i < _num ; i++)
           {
              if(!Init(_sem->Id(),i,_initVal[i]))
              {
                cerr << "init SemSet Sem :" << i << " error " << endl;
                exit(4);
              }
           }
        }
    }

    virtual shared_ptr<Semaphore> GetSem() override 
    {
        return _sem;
    }

private:
  bool Init(int semid,int num,int val) //哪个信号量集 哪个信号量 初始值
  {
      //1.自定义联合体对象
      union semun {
          int              val;    /* Value for SETVAL */
          struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
          unsigned short  *array;  /* Array for GETALL, SETALL */
          struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                      (Linux-specific) */
      }un;
      un.val = val;
      //2.调用semctl
      int n = ::semctl(semid,num,SETVAL,un);
      if(n < 0)
      {
          cerr << "semctl SETVAL error " << endl;
          return false;
      } 
      return true;
  }


private:
    key_t _key; //信号量集的键值
    int _perms ; //权限
    int _num;//信号量集中信号量个数
    vector<int> _initVal ;//初始值集合
    shared_ptr<Semaphore> _sem;    

};


//指挥者类
class Director
{
public:
  void Construct(shared_ptr<SemaphoreBuilder> builder,int flag,int perms = 0666,int num = defaultnum,vector<int> initVal = {1})
  {
    //1.创建信号量集
      builder->BuildKey();
      builder->SetPermission(perms);
      builder->SetSemNum(num);
      builder->Build(flag);
      //2.初始化:注意是创建的才用初始化，获取的人不需要
      builder->SetInitVal(initVal);
      if(flag == BUILD_SEM)
      builder->InitSem();
  }  
};